package com.linkoog.devtools.http.parser.spring.handler;

import com.intellij.psi.PsiAnnotation;
import com.intellij.psi.PsiAnnotationMemberValue;
import com.intellij.psi.PsiLiteral;
import com.intellij.psi.PsiLiteralExpression;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiParameter;
import com.linkoog.devtools.http.common.HttpServiceBody;
import com.linkoog.devtools.http.common.HttpServiceParam;
import com.linkoog.devtools.http.common.HttpParams;
import com.linkoog.devtools.http.parser.spring.common.SpringConstants;
import com.linkoog.devtools.type.LiteralType;
import com.linkoog.devtools.type.Type;
import com.linkoog.devtools.type.impl.*;
import org.jetbrains.annotations.NotNull;

import java.util.List;
import java.util.Map;

public class SpringHttpParamHandler {


    public HttpParams parseHttpMethod(@NotNull PsiMethod psiMethod, @NotNull List<Type> paramTypes){
        HttpParams httpParams = new HttpParams();


        final PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
        for (int i = 0; i < paramTypes.size(); i++) {
            Type paramType = paramTypes.get(i);
            PsiParameter psiParameter = parameters[i];
            if (paramType == null){
                //参数的类型，解析失败
                continue;
            }

            PsiAnnotation paramPsiAnnotation;
            if (paramType instanceof IgnoreType){
                continue;
            }

            //处理file类型参数
            if (paramType instanceof FileType){
                paramPsiAnnotation = findParamPsiAnnotation(psiMethod,psiParameter,i, SpringConstants.REQUEST_PARAM);
                HttpServiceParam httpServiceParam = handleHttpParam(psiParameter, i, paramPsiAnnotation, paramType);
                httpParams.getFileParams().add(httpServiceParam);
                continue;
            }

            //处理path参数
            paramPsiAnnotation = findParamPsiAnnotation(psiMethod, psiParameter, i, SpringConstants.PATH_VARIABLE);
            if (paramPsiAnnotation != null){
                final HttpServiceParam httpServiceParam = handleHttpParam(psiParameter, i, paramPsiAnnotation, paramType);
                httpParams.getPathParams().add(httpServiceParam);
                continue;
            }

            //处理header参数
            paramPsiAnnotation = findParamPsiAnnotation(psiMethod, psiParameter, i, SpringConstants.REQUEST_HEADER);
            if (paramPsiAnnotation != null){
                final HttpServiceParam httpServiceParam = handleHttpParam(psiParameter, i, paramPsiAnnotation, paramType);
                httpParams.getHeaderParams().add(httpServiceParam);
                continue;
            }

            //处理param参数
            paramPsiAnnotation = findParamPsiAnnotation(psiMethod, psiParameter, i, SpringConstants.REQUEST_PARAM);
            if (paramPsiAnnotation != null){
                final HttpServiceParam httpServiceParam = handleHttpParam(psiParameter, i, paramPsiAnnotation, paramType);
                httpParams.getQueryParams().add(httpServiceParam);
                continue;
            }

            //处理body参数
            paramPsiAnnotation = findParamPsiAnnotation(psiMethod, psiParameter, i, SpringConstants.REQUEST_BODY);
            if (paramPsiAnnotation != null){
                HttpServiceBody httpServiceBody = new HttpServiceBody();
                httpServiceBody.setType(paramType);
                httpParams.setBodyParam(httpServiceBody);
                continue;
            }

            //没有注解的参数
            if (paramType instanceof LiteralType || paramType instanceof ArrayType){
                HttpServiceParam httpServiceParam = new HttpServiceParam();
                httpServiceParam.setParamIndex(i);
                httpServiceParam.setParamName(psiParameter.getName());
                httpServiceParam.setRequire(false);
                httpServiceParam.setType(paramType);
                httpParams.getQueryParams().add(httpServiceParam);
            } else if (paramType instanceof ObjectType){
                ObjectType objectType = (ObjectType)paramType;

                final Map<String, Type> fieldTypeMap = objectType.getFieldTypeMap();
                final Map<String, ObjectFieldDoc> fieldDocMap = objectType.getFieldDocMap();
                if (fieldTypeMap == null || fieldTypeMap.isEmpty()){
                    continue;
                }


                for (String name : fieldTypeMap.keySet()) {
                    Type fieldType = fieldTypeMap.get(name);

                    HttpServiceParam httpServiceParam = new HttpServiceParam();
                    httpServiceParam.setParamIndex(i);
                    httpServiceParam.setParamName(name);
                    httpServiceParam.setRequire(false);
                    httpServiceParam.setType(fieldType);

                    ObjectFieldDoc objectFieldDoc = fieldDocMap.get(name);
                    if (objectFieldDoc != null){
                        httpServiceParam.setRequire(objectFieldDoc.isRequired());
                        httpServiceParam.setComment(objectFieldDoc.getDescription());
                        httpServiceParam.setExample(objectFieldDoc.getExample());
                    }

                    httpParams.getQueryParams().add(httpServiceParam);
                }
            }


        }

        return httpParams;
    }

    private HttpServiceParam handleHttpParam(PsiParameter psiParameter, int psiParamIndex, PsiAnnotation paramPsiAnnotation, Type type){
        HttpServiceParam httpServiceParam = new HttpServiceParam();
        httpServiceParam.setParamIndex(psiParamIndex);
        httpServiceParam.setParamName(parseNameAttribute(paramPsiAnnotation, psiParameter.getName()));
        httpServiceParam.setRequire(parseRequiredAttribute(paramPsiAnnotation));
        return httpServiceParam;
    }

    /**
     * 解析参数注解[REQUEST_PARAM,REQUEST_BODY,PATH_VARIABLE,REQUEST_HEADER,COOKIE_VALUE]的[Required]属性
     */
    private boolean parseRequiredAttribute(PsiAnnotation paramPsiAnnotation){
        if (paramPsiAnnotation == null){
            return false;
        }

        PsiAnnotationMemberValue psiAnnotationMemberValue = paramPsiAnnotation.findAttributeValue("required");
        if (psiAnnotationMemberValue == null){
            //没有设置，默认为true
            return true;
        }

        return "true".equals(psiAnnotationMemberValue.getText().trim());
    }

    /**
     * 解析参数注解[REQUEST_PARAM,REQUEST_BODY,PATH_VARIABLE,REQUEST_HEADER,COOKIE_VALUE]的名称属性
     */
    private String parseNameAttribute(PsiAnnotation paramPsiAnnotation, String defaultName){
        if (paramPsiAnnotation == null){
            return defaultName;
        }

        PsiAnnotationMemberValue psiAnnotationMemberValue = paramPsiAnnotation.findDeclaredAttributeValue("value");
        if (psiAnnotationMemberValue == null){
            psiAnnotationMemberValue = paramPsiAnnotation.findDeclaredAttributeValue("name");
        }

        if (psiAnnotationMemberValue == null){
            return defaultName;
        }

        if (psiAnnotationMemberValue instanceof PsiLiteralExpression){
            final Object value = ((PsiLiteral)psiAnnotationMemberValue).getValue();
            if (value instanceof String) return (String)value;
        }

        return psiAnnotationMemberValue.getText().trim();
    }

    private PsiAnnotation findParamPsiAnnotation(PsiMethod psiMethod, PsiParameter psiParameter, int psiParamIndex, String annoQualifiedName){
        if (psiParameter == null){
            return null;
        }

        PsiAnnotation psiAnnotation = findParamPsiAnnotation(psiParameter, annoQualifiedName);
        if (psiAnnotation != null){
            return psiAnnotation;
        }

        //再次从父方法的对应的参数上查找，注解
        PsiMethod[] superPsiMethodArray = psiMethod.findSuperMethods(true);
        if (superPsiMethodArray.length == 0){
            return null;
        }

        for (PsiMethod superPsiMethod : superPsiMethodArray) {
            PsiParameter superPsiParameter = superPsiMethod.getParameterList().getParameters()[psiParamIndex];
            psiAnnotation = findParamPsiAnnotation(superPsiParameter ,annoQualifiedName);
            if (psiAnnotation != null){
                return psiAnnotation;
            }
        }

        return null;
    }


    private PsiAnnotation findParamPsiAnnotation(PsiParameter psiParameter, String annoQualifiedName){
        if (psiParameter == null){
            return null;
        }

        PsiAnnotation psiAnnotation = psiParameter.getAnnotation(annoQualifiedName);
        if (psiAnnotation != null){
            return psiAnnotation;
        }

        return null;
    }

}
